home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / machserver / 1.098 / dev / devSCSITape.c < prev    next >
C/C++ Source or Header  |  1991-08-11  |  23KB  |  827 lines

  1. /* 
  2.  * devSCSITape.c --
  3.  *
  4.  *      The standard Open, Read, Write, IOControl, and Close operations
  5.  *      are defined here for the SCSI tape.
  6.  *
  7.  * Permission to use, copy, modify, and distribute this
  8.  * software and its documentation for any purpose and without
  9.  * fee is hereby granted, provided that the above copyright
  10.  * notice appear in all copies.  The University of California
  11.  * makes no representations about the suitability of this
  12.  * software for any purpose.  It is provided "as is" without
  13.  * express or implied warranty.
  14.  *
  15.  * Copyright 1986 Regents of the University of California
  16.  * All rights reserved.
  17.  */
  18.  
  19. #ifndef lint
  20. static char rcsid[] = "$Header: /sprite/src/kernel/dev/RCS/devSCSITape.c,v 9.6 91/06/21 18:27:59 kupfer Exp $ SPRITE (Berkeley)";
  21. #endif not lint
  22.  
  23.  
  24. #include "sprite.h"
  25. #include "stdio.h"
  26. #include "fs.h"
  27. #include "dev.h"
  28. #include "devInt.h"
  29. #include "scsi.h"
  30. #include "scsiDevice.h"
  31. #include "scsiTape.h"
  32. #include "devSCSITape.h"
  33. #include "dev/scsi.h"
  34. #include "stdlib.h"
  35. #include "bstring.h"
  36.  
  37. #include "dbg.h"
  38. int SCSITapeDebug = FALSE;
  39.  
  40. static ReturnStatus InitTapeDevice _ARGS_((Fs_Device *devicePtr,
  41.     ScsiDevice *devPtr));
  42. static void SetupCommand _ARGS_((ScsiDevice *devPtr, int command,
  43.     unsigned int code, unsigned int len, ScsiCmd *scsiCmdPtr));
  44.  
  45.  
  46. /*
  47.  *----------------------------------------------------------------------
  48.  *
  49.  * InitTapeDevice --
  50.  *
  51.  *    Initialize the device driver state for a SCSI Tape drive.
  52.  *
  53.  * Results:
  54.  *    SUCCESS.  If the tape driver is successfully initialized. A 
  55.  *    Sprite error code otherwise.
  56.  *
  57.  * Side effects:
  58.  *
  59.  *----------------------------------------------------------------------
  60.  */
  61. static ReturnStatus
  62. InitTapeDevice(devicePtr, devPtr)
  63.     Fs_Device *devicePtr;    /* Device info, unit number etc. */
  64.     ScsiDevice *devPtr;        /* Attached SCSI tape. */
  65. {
  66.     ScsiTape    tapeData;    
  67.     ScsiTape    *tapePtr;
  68.     ReturnStatus status;
  69.     int        i;
  70.  
  71.     /*
  72.     * Determine the type of device from the inquiry return by the
  73.     * attach. Reject device if not of tape type. If the target 
  74.     * didn't respond to the INQUIRY command we assume that it
  75.     * just a stupid tape.
  76.     */ 
  77.     if ((devPtr->inquiryLength > 0) &&
  78.     (((ScsiInquiryData *) (devPtr->inquiryDataPtr))->type != 
  79.                             SCSI_TAPE_TYPE)) {
  80.     return DEV_NO_DEVICE;
  81.     } 
  82.     /*
  83.      * Do a quick ready test on the device.
  84.      */
  85.     status = DevScsiTestReady(devPtr);
  86.     if (status != SUCCESS) {
  87.     return status;
  88.     }
  89.     if (devicePtr->data == (ClientData) NIL) {
  90.     tapePtr = &tapeData;
  91.     bzero((char *) tapePtr, sizeof(ScsiTape));
  92.     tapePtr->devPtr = devPtr;
  93.     tapePtr->state = SCSI_TAPE_CLOSED;
  94.     tapePtr->name = "SCSI Tape";
  95.     tapePtr->blockSize = SCSI_TAPE_DEFAULT_BLOCKSIZE;
  96.     tapePtr->tapeIOProc = DevSCSITapeFixedBlockIO;
  97.     tapePtr->errorProc = DevSCSITapeError;
  98.     tapePtr->specialCmdProc =  DevSCSITapeSpecialCmd;
  99.     } else {
  100.     tapePtr = (ScsiTape *) (devicePtr->data);
  101.     }
  102.     for (i = 0; i < devNumSCSITapeTypes; i++) {
  103.     status = (devSCSITapeAttachProcs[i])(devicePtr,devPtr,tapePtr);
  104.     if (status == SUCCESS) {
  105.         break;
  106.     }
  107.     }
  108.     /*
  109.      * Allocate and return the ScsiTape structure in the data field of the
  110.      * Fs_Device.
  111.      */
  112.     if ((status == SUCCESS) && devicePtr->data == (ClientData) NIL) { 
  113.     tapePtr = (ScsiTape *) malloc(sizeof(ScsiTape));
  114.     *tapePtr = tapeData;
  115.         devicePtr->data = (ClientData)tapePtr;
  116.     }
  117.     return(status);
  118. }
  119.  
  120. /*
  121.  *----------------------------------------------------------------------
  122.  *
  123.  * SetupCommand --
  124.  *
  125.  *    A variation on DevScsiGroup0Cmd that creates a control block
  126.  *    designed for tape drives.  SCSI tape drives read from the current
  127.  *    tape position, so there is only a block count, no offset.  There
  128.  *    is a special code that modifies the command in the tape control
  129.  *    block.  The value of the code is a function of the command and the
  130.  *    type of the tape drive (ugh.)  
  131.  *
  132.  * Results:
  133.  *    None.
  134.  *
  135.  * Side effects:
  136.  *    Set the various fields in the tape control block.
  137.  *
  138.  *----------------------------------------------------------------------
  139.  */
  140. static void
  141. SetupCommand(devPtr, command, code, len, scsiCmdPtr)
  142.     ScsiDevice    *devPtr;    /* Scsi device for command. */
  143.     int command;        /* One of SCSI_* commands */
  144.     unsigned int code;        /* Value for "code" field of command. The code
  145.                  * field is the low 4 bits of the 2nd byte. */
  146.     unsigned int len;        /* Length of the data in bytes or blocks. */
  147.     ScsiCmd    *scsiCmdPtr;    /* Command block to fill in. */
  148. {
  149.     /*
  150.      * Length field is only 3 bytes long in scsi command block. Mask off
  151.      * the upper bits (they will be set if the number is negative).
  152.      */
  153.     len &= 0x00ffffff;
  154.     DevScsiGroup0Cmd(devPtr, command, ((code&0xf) << 16) | (len>>8),
  155.             (len & 0xff), scsiCmdPtr);
  156. }
  157.  
  158.  
  159. /*
  160.  *----------------------------------------------------------------------
  161.  *
  162.  * DevSCSITapeError --
  163.  *
  164.  *    Map SCSI errors indicated by the sense data into Sprite ReturnStatus
  165.  *    and error message. This proceedure handles two types of 
  166.  *    sense data Class 0 and class 7.
  167.  *
  168.  * Results:
  169.  *    A sprite error code.
  170.  *
  171.  * Side effects:
  172.  *    None.
  173.  *
  174.  *----------------------------------------------------------------------
  175.  */
  176. ReturnStatus
  177. DevSCSITapeError(tapePtr, statusWord, senseLength, senseDataPtr)
  178.     ScsiTape     *tapePtr;    /* SCSI Tape that's complaining. */
  179.     unsigned int statusWord;    /* The status byte of the command. */
  180.     int         senseLength;    /* Length of SCSI sense data in bytes. */
  181.     char     *senseDataPtr;    /* Sense data. */
  182. {
  183.     unsigned char statusByte = statusWord;
  184.     ScsiStatus *statusPtr = (ScsiStatus *) &statusByte;
  185.     ScsiClass0Sense *sensePtr = (ScsiClass0Sense *) senseDataPtr;
  186.     char    *name = tapePtr->devPtr->locationName;
  187.     char    errorString[MAX_SCSI_ERROR_STRING];
  188.     ReturnStatus    status;
  189.  
  190.     /*
  191.      * Check for status byte to see if the command returned sense
  192.      * data. If no sense data exists then we only have the status
  193.      * byte to look at.
  194.      */
  195.     if (!statusPtr->check) {
  196.     if (SCSI_RESERVED_STATUS(statusByte) || statusPtr->intStatus) {
  197.         printf("Warning: %s at %s unknown status byte 0x%x\n",
  198.            tapePtr->name, name, statusByte);
  199.         return SUCCESS;
  200.     } 
  201.     if (statusPtr->busy) {
  202.         return DEV_OFFLINE;
  203.     }
  204.     return SUCCESS;
  205.     }
  206.     if (senseLength == 0) {
  207.      printf("Warning: %s at %s error: no sense data\n", tapePtr->name,name);
  208.      return DEV_NO_SENSE;
  209.     }
  210.     if (DevScsiMapClass7Sense(senseLength, senseDataPtr,&status, errorString)) {
  211.     ScsiClass7Sense    *s = (ScsiClass7Sense *) senseDataPtr;
  212.     if (errorString[0]) {
  213.          printf("Warning: %s at %s error: %s\n", tapePtr->name, name, 
  214.             errorString);
  215.     }
  216.     if (status == SUCCESS) {
  217.         if (s->fileMark) {
  218.         /*
  219.          * Hit the file mark after reading good data. Setting this 
  220.          * bit causes the next read to return zero bytes.
  221.          */
  222.         tapePtr->state |= SCSI_TAPE_AT_EOF;
  223.         } else if (s->endOfMedia) {
  224.         status = DEV_END_OF_TAPE;
  225.         }
  226.     }
  227.     return status;
  228.     }
  229.     /*
  230.      * If its not a class 7 error it must be Old style sense data..
  231.      */
  232.     if (sensePtr->error == SCSI_NO_SENSE_DATA) {        
  233.     status = SUCCESS;
  234.     } else {
  235.         register int class = (sensePtr->error & 0x70) >> 4;
  236.         register int code = sensePtr->error & 0xF;
  237.         register int addr;
  238.         addr = (sensePtr->highAddr << 16) |
  239.             (sensePtr->midAddr << 8) |
  240.             sensePtr->lowAddr;
  241.         printf("Warning: %s at %s: Sense error (%d-%d) at <%x> ",
  242.                  tapePtr->name, name, class, code, addr);
  243.         if (devScsiNumErrors[class] > code) {
  244.         printf("%s", devScsiErrors[class][code]);
  245.         }
  246.         printf("\n");
  247.         status = DEV_INVALID_ARG;
  248.     } 
  249.     return(status);
  250. }
  251.  
  252.  
  253. /*
  254.  *----------------------------------------------------------------------
  255.  *
  256.  * DevSCSITapeSpecialCmd --
  257.  *
  258.  *    Performance a special tape command on a SCSI Tape drive. This 
  259.  *    routine should work on any SCSI Tape drive adhering to the SCSI
  260.  *    common command set.
  261.  *
  262.  * Results:
  263.  *    The sprite return status.
  264.  *
  265.  * Side effects:
  266.  *    Command dependent.
  267.  *
  268.  *----------------------------------------------------------------------
  269.  */
  270.  
  271. ReturnStatus
  272. DevSCSITapeSpecialCmd(tapePtr, command, count)
  273.     ScsiTape    *tapePtr;    /* Target drive for command. */
  274.     int        command;    /* Command to be performed. */
  275.     int        count;        /* Argument to command. */
  276. {
  277.     ReturnStatus status;
  278.     ScsiCmd     scsiTapeCmd;
  279.     unsigned char statusByte;
  280.     int        senseLength;
  281.     char    senseBuffer[SCSI_MAX_SENSE_LEN];
  282.     unsigned int    code;
  283.     int            amountTransferred;
  284.     int        scsiCmd;
  285.  
  286.    code = 0;
  287.    switch (command) {
  288.     case IOC_TAPE_SKIP_FILES:
  289.     case IOC_TAPE_SKIP_BLOCKS: 
  290.     scsiCmd = SCSI_SPACE;
  291.     code = (command == IOC_TAPE_SKIP_FILES) ? 1 : 0;
  292.     break;
  293.     case IOC_TAPE_REWIND:
  294.     scsiCmd = SCSI_REWIND;
  295.     count = 0;
  296.     break;
  297.     case IOC_TAPE_WEOF:
  298.     scsiCmd = SCSI_WRITE_EOF;
  299.     break;
  300.     case IOC_TAPE_ERASE:
  301.     scsiCmd = SCSI_ERASE_TAPE;
  302.     count = 0;
  303.     break;
  304.     case IOC_TAPE_NO_OP:
  305.     scsiCmd = SCSI_TEST_UNIT_READY;
  306.     count = 0;
  307.     break;
  308.     case IOC_TAPE_RETENSION:
  309.     return DEV_INVALID_ARG;
  310.     default:
  311.     scsiCmd = 0;
  312.     panic("DevSCSITapeSpecialCmd: Unknown command %d\n", command);
  313.     }
  314.     SetupCommand(tapePtr->devPtr, scsiCmd, code, (unsigned)count, &scsiTapeCmd);
  315.     scsiTapeCmd.buffer = (char *) 0;
  316.     scsiTapeCmd.bufferLen = 0;
  317.     scsiTapeCmd.dataToDevice = FALSE;
  318.     senseLength = SCSI_MAX_SENSE_LEN;
  319.     status = DevScsiSendCmdSync(tapePtr->devPtr, &scsiTapeCmd, &statusByte,
  320.                 &amountTransferred, &senseLength, senseBuffer);
  321.     if (status == SUCCESS) {
  322.     status = (tapePtr->errorProc)(tapePtr,statusByte, senseLength, 
  323.                       senseBuffer);
  324.     }
  325.     return(status);
  326.  
  327. }
  328.  
  329.  
  330. /*
  331.  *----------------------------------------------------------------------
  332.  *
  333.  * DevSCSITapeVariableIO --
  334.  *
  335.  *      Low level routine to read or write an SCSI tape device using a
  336.  *    variable size block format.   Each IO involves some number of
  337.  *    bytes.
  338.  *
  339.  * Results:
  340.  *    The Sprite return status.
  341.  *
  342.  * Side effects:
  343.  *    Tape is written or read.
  344.  *
  345.  *----------------------------------------------------------------------
  346.  */
  347. ReturnStatus
  348. DevSCSITapeVariableIO(tapePtr,command, buffer, countPtr)
  349.     register ScsiTape *tapePtr;     /* State info for the tape */
  350.     int command;            /* SCSI_READ, SCSI_WRITE, etc. */
  351.     char *buffer;            /* Target buffer */
  352.     int *countPtr;            /* In/Out byte count. */
  353. {
  354.     ReturnStatus status;
  355.     ScsiCmd     scsiTapeCmd;
  356.     unsigned char statusByte;
  357.     int        senseLength;
  358.     char    senseBuffer[SCSI_MAX_SENSE_LEN];
  359.  
  360.  
  361.     /* 
  362.      * Setup the command, a code value of zero means variable block.
  363.      */
  364.     SetupCommand(tapePtr->devPtr, command, 0,  (unsigned)*countPtr, 
  365.         &scsiTapeCmd);
  366.     scsiTapeCmd.buffer = buffer;
  367.     scsiTapeCmd.bufferLen = *countPtr;
  368.     scsiTapeCmd.dataToDevice = (command == SCSI_WRITE);
  369.     senseLength = SCSI_MAX_SENSE_LEN;
  370.     status = DevScsiSendCmdSync(tapePtr->devPtr, &scsiTapeCmd, &statusByte,
  371.                 countPtr, &senseLength, senseBuffer);
  372.  
  373.     if (status == SUCCESS) {
  374.     status = (tapePtr->errorProc)(tapePtr,statusByte, senseLength, 
  375.                       senseBuffer);
  376.     }
  377.     return(status);
  378. }
  379.  
  380.  
  381. /*
  382.  *----------------------------------------------------------------------
  383.  *
  384.  * DevSCSITapeFixedBlockIO --
  385.  *
  386.  *      Low level routine to read or write an SCSI tape device using a
  387.  *    fixed size block format.   Each IO involves one or more tape blocks 
  388.  *    and must be  multiples of the underlying device block size.
  389.  *
  390.  * Results:
  391.  *    The Sprite return status.
  392.  *
  393.  * Side effects:
  394.  *    Tape is written or read.
  395.  *
  396.  *----------------------------------------------------------------------
  397.  */
  398. ReturnStatus
  399. DevSCSITapeFixedBlockIO(tapePtr, command, buffer, countPtr)
  400.     register ScsiTape *tapePtr;     /* State info for the tape */
  401.     int command;            /* SCSI_READ, SCSI_WRITE, etc. */
  402.     char *buffer;            /* Target buffer */
  403.     int *countPtr;            /* In/Out byte count. */
  404. {
  405.     ReturnStatus status;
  406.     ScsiCmd     scsiTapeCmd;
  407.     unsigned char statusByte;
  408.     int        senseLength;
  409.     char    senseBuffer[SCSI_MAX_SENSE_LEN];
  410.     int        lengthInBlocks;
  411.  
  412.    /*
  413.      * For simplicity reads and writes that are multiple of the block size
  414.      * are rejected.
  415.      */
  416.     if ((*countPtr % (tapePtr->blockSize)) != 0) {
  417.     *countPtr = 0;
  418.     return DEV_INVALID_ARG;
  419.     }
  420.     lengthInBlocks = *countPtr / tapePtr->blockSize;
  421.     /*
  422.      * Set up the command with a code value of 1 meaning fixed block.
  423.      */
  424.     SetupCommand(tapePtr->devPtr, command, 1, (unsigned)lengthInBlocks,
  425.          &scsiTapeCmd);
  426.     scsiTapeCmd.buffer = buffer;
  427.     scsiTapeCmd.bufferLen = *countPtr;
  428.     scsiTapeCmd.dataToDevice = (command == SCSI_WRITE);
  429.     senseLength = SCSI_MAX_SENSE_LEN;
  430.     status = DevScsiSendCmdSync(tapePtr->devPtr, &scsiTapeCmd, &statusByte,
  431.                 countPtr, &senseLength, senseBuffer);
  432.  
  433.     if (status == SUCCESS) {
  434.     status = (tapePtr->errorProc)(tapePtr,statusByte, senseLength, 
  435.                       senseBuffer);
  436.     }
  437.     return(status);
  438. }
  439.  
  440. /*
  441.  *----------------------------------------------------------------------
  442.  *
  443.  * DevSCSITapeOpen --
  444.  *
  445.  *    Open a SCSI tape drive as a file.  This routine verifies the
  446.  *    drives existance and sets any special mode flags.
  447.  *
  448.  * Results:
  449.  *    SUCCESS if the tape is on-line.
  450.  *
  451.  * Side effects:
  452.  *    None.
  453.  *
  454.  *----------------------------------------------------------------------
  455.  */
  456. /* ARGSUSED */
  457. ReturnStatus
  458. DevSCSITapeOpen(devicePtr, useFlags, token, flagsPtr)
  459.     Fs_Device *devicePtr;    /* Device info, unit number etc. */
  460.     int useFlags;        /* Flags from the stream being opened */
  461.     Fs_NotifyToken token;    /* Call-back token for input, unused here */
  462.     int        *flagsPtr;    /* OUT: Device flags. */
  463. {
  464.     ReturnStatus status;
  465.     ScsiDevice *devPtr;
  466.     ScsiTape *tapePtr;
  467.  
  468.     tapePtr = (ScsiTape *) (devicePtr->data);
  469.     if (tapePtr == (ScsiTape *) NIL) {
  470.     /*
  471.      * Ask the HBA to set up the path to the device with FIFO ordering
  472.      * of requests.
  473.      */
  474.     devPtr = DevScsiAttachDevice(devicePtr, DEV_QUEUE_FIFO_INSERT);
  475.     if (devPtr == (ScsiDevice *) NIL) {
  476.         return DEV_NO_DEVICE;
  477.     }
  478.     } else { 
  479.     /*
  480.      * If the tapePtr is already attached to the device it must be
  481.      * busy.
  482.      */
  483.      return(FS_FILE_BUSY);
  484.     }
  485.     status = InitTapeDevice(devicePtr, devPtr);
  486.     return(status);
  487. }
  488.  
  489. /*
  490.  *----------------------------------------------------------------------
  491.  *
  492.  * DevSCSITapeRead --
  493.  *
  494.  *    Read from a raw SCSI Tape.  The offset is ignored because
  495.  *    you can't seek the SCSI tape drive, only rewind it.
  496.  *
  497.  * Results:
  498.  *    The return status of the read.
  499.  *
  500.  * Side effects:
  501.  *    The process will sleep waiting for the I/O to complete.
  502.  *
  503.  *----------------------------------------------------------------------
  504.  */
  505.  
  506. /*ARGSUSED*/
  507. ReturnStatus
  508. DevSCSITapeRead(devicePtr, readPtr, replyPtr)
  509.     Fs_Device *devicePtr;    /* Handle for raw SCSI tape device */
  510.     Fs_IOParam    *readPtr;    /* Read parameter block */
  511.     Fs_IOReply    *replyPtr;    /* Return length and signal */ 
  512. {
  513.     ReturnStatus error;    
  514.     ScsiTape *tapePtr;
  515.     int    totalTransfer;
  516.     int transferSize;
  517.     int    maxXfer;
  518.  
  519.     tapePtr = (ScsiTape *)(devicePtr->data);
  520.     if (tapePtr->state & SCSI_TAPE_AT_EOF) {
  521.     /*
  522.      * Force the use of the SKIP_FILES control to get past the end of
  523.      * the file on tape.
  524.      */
  525.     replyPtr->length = 0;
  526.     return(SUCCESS);
  527.     }
  528.     /*
  529.      * Break up the IO into piece the device/HBA can handle.
  530.      */
  531.     error = SUCCESS;
  532.     maxXfer = tapePtr->devPtr->maxTransferSize;
  533.     totalTransfer = 0;
  534.     while((readPtr->length > 0) && (error == SUCCESS)) {  
  535.     int    byteCount;
  536.     transferSize = (readPtr->length > maxXfer) ? maxXfer : readPtr->length;
  537.     byteCount = transferSize;
  538.     error = (tapePtr->tapeIOProc)(tapePtr, SCSI_READ,
  539.                   readPtr->buffer + totalTransfer, &byteCount);
  540.     /*
  541.      * A short read implies we hit end of file or end of tape. 
  542.      */
  543.     totalTransfer += byteCount;
  544.     readPtr->length -= byteCount;
  545.     if (byteCount < transferSize) {
  546.         break;
  547.     }
  548.     }
  549.     replyPtr->length = totalTransfer;
  550.     /*
  551.      * Special check against funky end-of-file situations.  The Emulex tape
  552.      * doesn't compute a correct residual when it hits the file mark
  553.      * on the tape.
  554.      */
  555.     if (error == DEV_END_OF_TAPE) {
  556.     replyPtr->length = 0;
  557.     error = SUCCESS;
  558.     }
  559.     return(error);
  560. }
  561.  
  562. /*
  563.  *----------------------------------------------------------------------
  564.  *
  565.  * DevSCSITapeWrite --
  566.  *
  567.  *    Write to a raw SCSI tape.
  568.  *
  569.  * Results:
  570.  *    A Sprite error code.
  571.  *
  572.  * Side effects:
  573.  *    None.
  574.  *
  575.  *----------------------------------------------------------------------
  576.  */
  577.  
  578. /*ARGSUSED*/
  579. ReturnStatus
  580. DevSCSITapeWrite(devicePtr, writePtr, replyPtr)
  581.     Fs_Device *devicePtr;    /* Handle of raw tape device */
  582.     Fs_IOParam    *writePtr;    /* Standard write parameter block */
  583.     Fs_IOReply    *replyPtr;    /* Return length and signal */
  584. {
  585.     ReturnStatus error;    
  586.     ScsiTape *tapePtr;
  587.     int totalTransfer;
  588.     int transferSize;
  589.     int    maxXfer;
  590.  
  591.     tapePtr = (ScsiTape *)(devicePtr->data);
  592.     /*
  593.      * Break up the IO into piece the device/HBA can handle.
  594.      */
  595.     error = SUCCESS;
  596.     maxXfer = tapePtr->devPtr->maxTransferSize;
  597.     totalTransfer = 0;
  598.     while((writePtr->length > 0) && (error == SUCCESS)) {  
  599.     int    byteCount;
  600.     transferSize = (writePtr->length > maxXfer) ? maxXfer : writePtr->length;
  601.     byteCount = transferSize;
  602.     error = (tapePtr->tapeIOProc)(tapePtr, SCSI_WRITE,
  603.                   writePtr->buffer + totalTransfer, &byteCount);
  604.     /*
  605.      * A short write implies we hit end of tape. 
  606.      */
  607.     totalTransfer += byteCount;
  608.     writePtr->length -= transferSize;
  609.     if (byteCount < transferSize) {
  610.         break;
  611.     }
  612.     }
  613.     replyPtr->length = totalTransfer;
  614.     if (error == SUCCESS) {
  615.     tapePtr->state |= SCSI_TAPE_WRITTEN;
  616.     }
  617.     return(error);
  618. }
  619.  
  620. /*
  621.  *----------------------------------------------------------------------
  622.  *
  623.  * DevSCSITapeIOControl --
  624.  *
  625.  *    Do a special operation on a raw SCSI Tape.
  626.  *
  627.  * Results:
  628.  *    None.
  629.  *
  630.  * Side effects:
  631.  *    None.
  632.  *
  633.  *----------------------------------------------------------------------
  634.  */
  635. /*ARGSUSED*/
  636. ReturnStatus
  637. DevSCSITapeIOControl(devicePtr, ioctlPtr, replyPtr)
  638.     Fs_Device *devicePtr;
  639.     Fs_IOCParam *ioctlPtr;    /* Standard I/O Control parameter block */
  640.     Fs_IOReply *replyPtr;    /* Size of outBuffer and returned signal */
  641. {
  642.     ScsiTape *tapePtr;
  643.     ReturnStatus status = SUCCESS;
  644.  
  645.     tapePtr = (ScsiTape *)(devicePtr->data);
  646.      if ((ioctlPtr->command & ~0xffff) == IOC_SCSI) {
  647.      status = DevScsiIOControl(tapePtr->devPtr, ioctlPtr, replyPtr);
  648.      return status;
  649.  
  650.      }
  651.  
  652.     switch(ioctlPtr->command) {
  653.     case IOC_REPOSITION: {
  654.         Ioc_RepositionArgs *repoArgsPtr;
  655.         repoArgsPtr = (Ioc_RepositionArgs *)ioctlPtr->inBuffer;
  656.  
  657.         switch (repoArgsPtr->base) {
  658.         case IOC_BASE_ZERO:
  659.             if (repoArgsPtr->offset != 0) {
  660.             return(DEV_INVALID_ARG);
  661.             }
  662.             status = (tapePtr->specialCmdProc)(tapePtr,
  663.                           IOC_TAPE_REWIND, 1);
  664.             break;
  665.         case IOC_BASE_CURRENT:
  666.             status = DEV_INVALID_ARG;
  667.             break;
  668.         case IOC_BASE_EOF:
  669.             if (repoArgsPtr->offset != 0) {
  670.             status = DEV_INVALID_ARG;
  671.             } else if ((tapePtr->state & SCSI_TAPE_WRITTEN) == 0) {
  672.             /*
  673.              * If not atlready at the end of the tape by writing,
  674.              * space to the end of the current file.
  675.              */
  676.             status = (tapePtr->specialCmdProc)(tapePtr, 
  677.                            IOC_TAPE_SKIP_FILES, 1);
  678.             }
  679.             break;
  680.         }
  681.         tapePtr->state &= ~SCSI_TAPE_WRITTEN;
  682.         break;
  683.     }
  684.     case IOC_TAPE_COMMAND: {
  685.         Dev_TapeCommand *cmdPtr = (Dev_TapeCommand *)ioctlPtr->inBuffer;
  686.         if (ioctlPtr->inBufSize < sizeof(Dev_TapeCommand)) {
  687.         return(DEV_INVALID_ARG);
  688.         }
  689.         tapePtr->state &= ~SCSI_TAPE_WRITTEN;
  690.         switch (cmdPtr->command) {
  691.         case IOC_TAPE_WEOF: {
  692.             status = (tapePtr->specialCmdProc)(tapePtr, IOC_TAPE_WEOF,
  693.                             cmdPtr->count);
  694.             break;
  695.         }
  696.         case IOC_TAPE_RETENSION: {
  697.             status = (tapePtr->specialCmdProc)(tapePtr,
  698.                           IOC_TAPE_RETENSION, 1);
  699.             break;
  700.         }
  701.         case IOC_TAPE_OFFLINE:
  702.         case IOC_TAPE_REWIND: {
  703.             status = (tapePtr->specialCmdProc)(tapePtr,
  704.                           IOC_TAPE_REWIND, 1);
  705.             break;
  706.         }
  707.         case IOC_TAPE_SKIP_BLOCKS: {
  708.             status = (tapePtr->specialCmdProc)(tapePtr, 
  709.                     IOC_TAPE_SKIP_BLOCKS, cmdPtr->count);
  710.             if (status == DEV_END_OF_TAPE) {
  711.             status = SUCCESS;
  712.             }
  713.             break;
  714.         case IOC_TAPE_SKIP_FILES:
  715.             status = (tapePtr->specialCmdProc)(tapePtr, 
  716.                     IOC_TAPE_SKIP_FILES,  cmdPtr->count);
  717.             if (status == DEV_END_OF_TAPE) {
  718.             status = SUCCESS;
  719.             }
  720.             break;
  721.         }
  722.         case IOC_TAPE_BACKUP_BLOCKS:
  723.         case IOC_TAPE_BACKUP_FILES:
  724.             status = DEV_INVALID_ARG;
  725.             break;
  726.         case IOC_TAPE_ERASE: {
  727.             status = (tapePtr->specialCmdProc)(tapePtr, 
  728.                             IOC_TAPE_ERASE,1);
  729.             break;
  730.         }
  731.         case IOC_TAPE_NO_OP: {
  732.             status = (tapePtr->specialCmdProc)(tapePtr, 
  733.                               IOC_TAPE_NO_OP,1);
  734.             break;
  735.         }
  736.         }
  737.         break;
  738.     }
  739.     case IOC_TAPE_STATUS: {
  740.         return(DEV_INVALID_ARG);
  741.     }
  742.         /*
  743.          * No tape specific bits are set this way.
  744.          */
  745.     case    IOC_GET_FLAGS:
  746.     case    IOC_SET_FLAGS:
  747.     case    IOC_SET_BITS:
  748.     case    IOC_CLEAR_BITS:
  749.         return(SUCCESS);
  750.  
  751.     case    IOC_GET_OWNER:
  752.     case    IOC_SET_OWNER:
  753.         return(GEN_NOT_IMPLEMENTED);
  754.  
  755.     case    IOC_TRUNCATE:
  756.         /*
  757.          * Could make this to an erase tape...
  758.          */
  759.         return(GEN_INVALID_ARG);
  760.  
  761.     case    IOC_LOCK:
  762.     case    IOC_UNLOCK:
  763.         return(GEN_NOT_IMPLEMENTED);
  764.  
  765.     case    IOC_NUM_READABLE:
  766.         return(GEN_NOT_IMPLEMENTED);
  767.  
  768.     case    IOC_MAP:
  769.         return(GEN_NOT_IMPLEMENTED);
  770.         
  771.     default:
  772.         return(GEN_INVALID_ARG);
  773.     }
  774.     return(status);
  775. }
  776.  
  777. /*
  778.  *----------------------------------------------------------------------
  779.  *
  780.  * DevSCSITapeClose --
  781.  *
  782.  *    Close a raw SCSI tape file.  This checks the unit number to
  783.  *    determine if the tape should be rewound.  Units 0 and 8
  784.  *    are rewind always, units 1 and 9 are no-rewind..
  785.  *
  786.  * Results:
  787.  *    None.
  788.  *
  789.  * Side effects:
  790.  *    None.
  791.  *
  792.  *----------------------------------------------------------------------
  793.  */
  794. /*ARGSUSED*/
  795. ReturnStatus
  796. DevSCSITapeClose(devicePtr, useFlags, openCount, writerCount)
  797.     Fs_Device    *devicePtr;
  798.     int        useFlags;    /* FS_READ | FS_WRITE */
  799.     int        openCount;    /* Number of times device open. */
  800.     int        writerCount;    /* Number of times device open for writing. */
  801. {
  802.     ScsiTape *tapePtr;
  803.     ReturnStatus status = SUCCESS;
  804.  
  805.     tapePtr = (ScsiTape *)(devicePtr->data);
  806.     if (openCount > 0) {
  807.     return(SUCCESS);
  808.     }
  809.     if (tapePtr->state & SCSI_TAPE_WRITTEN) {
  810.     /*
  811.      * Make sure an end-of-file mark is at the end of the file on tape.
  812.      */
  813.     status = (tapePtr->specialCmdProc)(tapePtr, IOC_TAPE_WEOF,1);
  814.     }
  815.     /*
  816.      * Use the unit number to indicate rewind or no-rewind.  An
  817.      * ``even'' number (0 and 8) means rewind.
  818.      */
  819.     if ((devicePtr->unit % 2) == 0) {
  820.     status = (tapePtr->specialCmdProc)(tapePtr, IOC_TAPE_REWIND,1);
  821.     }
  822.     (void) DevScsiReleaseDevice(tapePtr->devPtr);
  823.     free((char *)tapePtr);
  824.     devicePtr->data = (ClientData) NIL;
  825.     return(status);
  826. }
  827.